package com.sokobanboxpro.game.gameplay;

import com.sokobanboxpro.game.entity.*;

/**
 * Уровень игры. Один уровень может содержать пол, ящики, точки, стены и игрока
 *
 * @author Esr
 */
public class Level implements Cloneable {

		/**
		 * Ящики.
		 */
		private Box[] boxes;
		/**
		 * Игрок.
		 */
		private Player player;
		/**
		 * Точки.
		 */
		private Point[] points;
		/**
		 * Стены.
		 */
		private Wall[] walls;
		/**
		 * Пол.
		 */
		private Floor[] floors;
		/**
		 * Ширена уровня.
		 */
		private int width;
		/**
		 * Высота уровня.
		 */
		private int height;

		/**
		 * Создает новый уровень.
		 *
		 * @param width Ширена
		 * @param height Высота
		 * @param blocks Массив блоков, значение, которых заданы по спецификации
		 * файла уровней
		 * @throws Exception
		 */
		public Level(int width, int height, byte[] blocks) throws Exception {

				this.width = width;
				this.height = height;

				int boxesCount = 0;
				int pointsCount = 0;
				int wallsCount = 0;
				int floorsCount = 0;

				for (int i = 0; i < blocks.length; i++) {

						switch (blocks[i]) {

								case 4:
										floorsCount++;
										break;
								case 2:
										floorsCount++;
										break;

								case 5:
										boxesCount++;
										floorsCount++;
										break;

								case 6:
										pointsCount++;
										break;

								case 1:
										wallsCount++;
										break;

								case 10:
										boxesCount++;
										pointsCount++;
										break;

								case 14:
										pointsCount++;
										break;

						}

				}

				boxes = new Box[boxesCount];
				points = new Point[pointsCount];
				walls = new Wall[wallsCount];
				floors = new Floor[floorsCount];
				boxesCount = 0;
				pointsCount = 0;
				wallsCount = 0;
				floorsCount = 0;

				if (blocks.length != height * width) {
						throw new Exception("Количество блоков не совпадает");
				}

				for (int i = 0; i < height; i++) {
						for (int j = 0; j < width; j++) {

								switch (blocks[i * width + j]) {
										case 2:
												floors[floorsCount++] = new Floor(j, i);
												break;

										case 4:
												player = new Player(j, i);
												floors[floorsCount++] = new Floor(j, i);
												break;

										case 5:
												boxes[boxesCount++] = new Box(j, i);
												floors[floorsCount++] = new Floor(j, i);
												break;

										case 6:
												points[pointsCount++] = new Point(j, i);
												break;

										case 1:
												walls[wallsCount++] = new Wall(j, i);
												break;

										case 10:
												boxes[boxesCount++] = new Box(j, i);
												points[pointsCount++] = new Point(j, i);
												break;

										case 14:
												points[pointsCount++] = new Point(j, i);
												player = new Player(j, i);
												break;

								}
						}

				}

		}

		/**
		 * Получить массив ящиков.
		 *
		 * @return Ящики
		 */
		public Box[] getBoxes() {
				return boxes;
		}

		/**
		 * Установить массив ящиков.
		 *
		 * @param boxes Массив ящиков
		 */
		public void setBoxes(Box[] boxes) {
				this.boxes = boxes;
		}

		/**
		 * Получить игрока.
		 *
		 * @return Игрок
		 */
		public Player getPlayer() {
				return player;
		}

		/**
		 * Уставноить нового игрока.
		 *
		 * @param player Новый игрок
		 */
		public void setPlayer(Player player) {
				this.player = player;
		}

		/**
		 * Получить массив точек.
		 *
		 * @return Массив точек
		 */
		public Point[] getPoints() {
				return points;
		}

		/**
		 * Установить массив точек.
		 *
		 * @param points Массив точек
		 */
		public void setPoints(Point[] points) {
				this.points = points;
		}

		/**
		 * Получить массив стен.
		 *
		 * @return Массив стен
		 */
		public Wall[] getWalls() {
				return walls;
		}

		/**
		 * Установить массив стен.
		 *
		 * @param walls Массив стен
		 */
		public void setWalls(Wall[] walls) {
				this.walls = walls;
		}

		/**
		 * Получить массив пола.
		 *
		 * @return Массив пола
		 */
		public Floor[] getFloors() {
				return floors;
		}

		/**
		 * Получить ширену уровня(в блоках).
		 *
		 * @return Ширена уровня
		 */
		public int getWidth() {
				return width;
		}

		/**
		 * Получить высоту уровня(в блоках).
		 *
		 * @return Высота уровня
		 */
		public int getHeight() {
				return height;
		}

		/**
		 * Найти ящик на позиции.
		 *
		 * @param x Количество клеток по горизонтали
		 * @param y Количество клеток по вертикали
		 * @return Ящик или null если ящика нету
		 */
		public Box findBoxAtPosition(int x, int y) {

				for (Box box : boxes) {

						if (box.getX() == x && box.getY() == y) {
								return box;
						}

				}

				return null;

		}

		/**
		 * Найти стену на позиции.
		 *
		 * @param x Количество клеток по горизонтали
		 * @param y Количество клеток по вертикали
		 * @return Стену или null если стены нету
		 */
		public Wall findWallAtPosition(int x, int y) {

				for (Wall wall : walls) {

						if (wall.getX() == x && wall.getY() == y) {
								return wall;
						}

				}

				return null;

		}

		/**
		 * Найти точку на позиции.
		 *
		 * @param x Количество клеток по горизонтали
		 * @param y Количество клеток по вертикали
		 * @return Точку или null если точки нету
		 */
		public Point findPointAtPosition(int x, int y) {

				for (Point point : points) {

						if (point.getX() == x && point.getY() == y) {
								return point;
						}

				}

				return null;

		}

		/**
		 * Найти пол на позиции.
		 *
		 * @param x Количество клеток по горизонтали
		 * @param y Количество клеток по вертикали
		 * @return Пол или null если пола нету
		 */
		public Floor findFloorAtPosition(int x, int y) {

				for (Floor floor : floors) {

						if (floor.getX() == x && floor.getY() == y) {
								return floor;
						}

				}

				return null;

		}

		/**
		 * Клонирует уровень.
		 *
		 * @return Клонированый уровень
		 */
		@Override
		public Object clone() {//переопределение метода

				try {

						Box[] newBoxes = new Box[boxes.length];
						Player newPlayer = new Player(player.getX(), player.getY());
						Point[] newPoints = new Point[points.length];
						Wall[] newWalls = new Wall[walls.length];
						Floor[] newFloors = new Floor[floors.length];

						for (int i = 0; i < newBoxes.length; i++) {
								newBoxes[i] = new Box(boxes[i].getX(), boxes[i].getY());
						}

						for (int i = 0; i < newPoints.length; i++) {
								newPoints[i] = new Point(points[i].getX(), points[i].getY());
						}

						for (int i = 0; i < newWalls.length; i++) {
								newWalls[i] = new Wall(walls[i].getX(), walls[i].getY());
						}

						for (int i = 0; i < newFloors.length; i++) {
								newFloors[i] = new Floor(floors[i].getX(), floors[i].getY());
						}


						Level level = (Level) super.clone();

						level.boxes = newBoxes;
						level.player = newPlayer;
						level.floors = newFloors;
						level.walls = newWalls;
						level.points = newPoints;



						return level;

				} catch (CloneNotSupportedException e) {

						throw new AssertionError("Невозможно!");


				}

		}

}
